﻿Imports System.Linq
Imports Equipment.Core
Imports Equipment.DataAccess
Imports Equipment.Business
Imports log4net

Public Class WorkOrderManager
    Private Shared logger As ILog = LogManager.GetLogger(GetType(WorkOrderManager))

#Region "Public Section"


    Public Shared Function GetAllByStatusFor(ByVal forUser As IUserInfo, ByVal dueDateFrom As DateTime, ByVal dueDateTo As DateTime, ByVal status As WorkOrderStatusType) As IEnumerable(Of WorkOrder)
        Return GetFilteredByStatusFor(forUser, 0, 0, 0, dueDateFrom, dueDateTo, status)
    End Function

    Public Shared Function GetAllByStatusFor(ByVal forUser As IUserInfo, ByVal status As WorkOrderStatusType) As IEnumerable(Of WorkOrder)
        If forUser.IsSuperUser Then
            Return WorkOrder.RetrieveAllByStatus(status)
        ElseIf forUser.IsSiteAdmin Then
            Return WorkOrder.RetrieveAllBySiteAndStatus(forUser.SiteID, status)
        Else
            Return WorkOrder.RetrieveAllByStatusFor(forUser.GroupIDs, status)
        End If

    End Function

    Public Shared Function GetAllFor(ByVal forUser As IUserInfo) As IEnumerable(Of WorkOrder)
        If forUser.IsSuperUser Then
            Return WorkOrder.RetrieveAll()
        ElseIf forUser.IsSiteAdmin Then
            Return WorkOrder.RetrieveAllBySiteID(forUser.SiteID)
        Else
            Return WorkOrder.RetrieveAllFor(forUser.GroupIDs)
        End If
    End Function

    Public Shared Function GetAllFor(ByVal forUser As IUserInfo, ByVal dueDateFrom As DateTime, ByVal dueDateTo As DateTime) As IEnumerable(Of WorkOrder)
        If forUser.IsSuperUser Then
            Return WorkOrder.RetrieveAll(dueDateFrom, dueDateTo)
        ElseIf forUser.IsSiteAdmin Then
            Return WorkOrder.RetrieveAllBySiteID(forUser.SiteID, dueDateFrom, dueDateTo)
        Else
            Return WorkOrder.RetrieveAllFor(forUser.GroupIDs, dueDateFrom, dueDateTo)
        End If
    End Function

    Public Shared Function GetAllForReview(ByVal forUser As IUserInfo, ByVal dueDateFrom As DateTime, ByVal dueDateTo As DateTime) As IEnumerable(Of WorkOrder)
        If forUser.IsSuperUser Then
            Return WorkOrder.RetrieveAllByStatuses(WorkOrderStatusType.ReadyForReview, WorkOrderStatusType.ReadyForReviewWithExceptions, dueDateFrom, dueDateTo)
        ElseIf forUser.IsSiteAdmin Then
            Return WorkOrder.RetrieveAllBySiteAndStatuses(forUser.SiteID, dueDateFrom, dueDateTo, _
                                                            WorkOrderStatusType.ReadyForReview, _
                                                                WorkOrderStatusType.ReadyForReviewWithExceptions)
        Else
            Return WorkOrder.RetrieveAllByStatusesFor(forUser.RoleIDs, dueDateFrom, dueDateTo, WorkOrderStatusType.ReadyForReview, _
                                                        WorkOrderStatusType.ReadyForReviewWithExceptions)
        End If
    End Function

    Public Shared Function GetFilteredForReview(ByVal forUser As IUserInfo, ByVal assetTypeID As Integer, ByVal departmentID As Integer, _
                                                    ByVal groupID As Integer, ByVal dueDateFrom As DateTime, ByVal dueDateTo As DateTime) As IEnumerable(Of WorkOrder)

        If forUser.IsSuperUser Then
            Return WorkOrder.RetrieveAllByStatuses(assetTypeID, departmentID, groupID, _
                                                    dueDateFrom, dueDateTo, WorkOrderStatusType.ReadyForReview, _
                                                        WorkOrderStatusType.ReadyForReviewWithExceptions)
        ElseIf forUser.IsSiteAdmin Then
            Return WorkOrder.RetrieveAllBySiteAndStatuses(forUser.SiteID, assetTypeID, departmentID, groupID, _
                                                            dueDateFrom, dueDateTo, WorkOrderStatusType.ReadyForReview, _
                                                                WorkOrderStatusType.ReadyForReviewWithExceptions)
        Else
            Return WorkOrder.RetrieveAllBySiteAndStatusesAndReviewRole(forUser.SiteID, assetTypeID, departmentID, _
                                                                        groupID, dueDateFrom, dueDateTo, WorkOrderStatusType.ReadyForReview, _
                                                                            WorkOrderStatusType.ReadyForReviewWithExceptions)
        End If

    End Function

    Public Shared Function GetFilteredForReview(ByVal forUser As IUserInfo, ByVal assetTypeID As Integer, ByVal departmentID As Integer, _
                                                ByVal groupID As Integer, ByVal dueDateFrom As DateTime, ByVal dueDateTo As DateTime, ByVal status As WorkOrderStatusType) As IEnumerable(Of WorkOrder)

        If forUser.IsSuperUser Then
            Return WorkOrder.RetrieveAllByStatus(assetTypeID, departmentID, groupID, _
                                                    dueDateFrom, dueDateTo, status)
        ElseIf forUser.IsSiteAdmin Then
            Return WorkOrder.RetrieveAllBySiteAndStatuses(forUser.SiteID, assetTypeID, departmentID, groupID, _
                                                            dueDateFrom, dueDateTo, status, status)
        Else
            Return WorkOrder.RetrieveAllBySiteAndStatusesAndReviewRole(forUser.SiteID, assetTypeID, departmentID, _
                                                                        groupID, dueDateFrom, dueDateTo, status, status)
        End If

    End Function

    Public Shared Function GetFilteredFor(ByVal forUser As IUserInfo, ByVal assetTypeID As Integer, ByVal departmentID As Integer, _
                                            ByVal groupID As Integer, ByVal dueDateFrom As DateTime, ByVal dueDateTo As DateTime) As IEnumerable(Of WorkOrder)
        If forUser.IsSuperUser Then
            Return WorkOrder.RetrieveAll(assetTypeID, departmentID, groupID, dueDateFrom, dueDateTo)
        ElseIf forUser.IsSiteAdmin Then
            Return WorkOrder.RetrieveAllBySiteID(forUser.SiteID, assetTypeID, departmentID, groupID, dueDateFrom, dueDateTo)
        Else
            Return WorkOrder.RetrieveAllFor(forUser.GroupIDs, assetTypeID, departmentID, groupID, dueDateFrom, dueDateTo)
        End If
    End Function

    Public Shared Function GetFilteredByStatusFor(ByVal forUser As IUserInfo, ByVal assetTypeID As Integer, ByVal departmentID As Integer, _
                                        ByVal groupID As Integer, ByVal dueDateFrom As DateTime, ByVal dueDateTo As DateTime, ByVal status As WorkOrderStatusType) As IEnumerable(Of WorkOrder)
        If forUser.IsSuperUser Then
            Return WorkOrder.RetrieveAllByStatus(assetTypeID, departmentID, groupID, dueDateFrom, dueDateTo, status)
        ElseIf forUser.IsSiteAdmin Then
            Return WorkOrder.RetrieveAllBySiteIDAndStatus(forUser.SiteID, assetTypeID, departmentID, groupID, dueDateFrom, dueDateTo, status)
        Else
            Return WorkOrder.RetrieveAllByStatusFor(forUser.GroupIDs, assetTypeID, departmentID, groupID, dueDateFrom, dueDateTo, status)
        End If
    End Function

    Public Shared Sub CreateWorkOrdersBySchedule()
        Dim assets = Asset.RetrieveAllActive()

        Dim isSundayNow = SystemHelper.IsSundayNow()
        Dim isSaturdayNow = SystemHelper.IsSaturdayNow()
        Dim isHolidayNow = Holiday.IsExistForDate(DateTime.UtcNow.Date)

        For Each assetToCheck In assets
            If IsDepartmentActive(assetToCheck.SiteDepartment) Then
                CreateWorkOrder(assetToCheck, isSundayNow, isSaturdayNow, isHolidayNow)
            End If
        Next

    End Sub

#End Region

#Region "Private Section"
    Private Shared Function IsDepartmentActive(ByVal department As SiteDepartment) As Boolean
        If department.IsActive AndAlso department.ParentID <> 0 Then
            Dim site As SiteDepartment = SiteDepartment.GetByPrimaryKey(department.ParentID)
            Return site.IsActive
        End If
        Return False
    End Function

    Private Shared Sub CreateWorkOrder(ByRef assetToCheck As Asset, ByVal isSundayNow As Boolean, ByVal isSaturdayNow As Boolean, ByVal isHolidayNow As Boolean)
        Dim assetTimeZoneID As String = assetToCheck.SiteDepartment.Parent.TimeZoneID
        Dim assetTime As DateTime = TimeZoneManager.ConvertToCustomTimeFor(DateTime.UtcNow, assetTimeZoneID)

        For Each req In assetToCheck.Requirements
            If WorkOrder.NeedToBeCreatedBasedOn(assetToCheck.AssetID, req) AndAlso IsStartDateAllow(assetToCheck, req) Then

                If (isHolidayNow = True AndAlso req.UseHoliday) Or (isSundayNow = True AndAlso req.UseSundays) Or (isSaturdayNow = True AndAlso req.UseSaturdays) Or _
                     (isSundayNow = False AndAlso isSaturdayNow = False AndAlso isHolidayNow = False AndAlso req.UseNotHolidayWeekday) Then

                    Dim assetReq As AssetRequirement = AssetRequirement.GetBy(assetToCheck.AssetID, req.ID)

                    If assetReq IsNot Nothing Then

                        Dim newWorkOrder As New WorkOrder()
                        Dim Details As New List(Of String)
                        newWorkOrder.AssetID = assetToCheck.AssetID
                        newWorkOrder.IsAutomaticallyCreated = True
                        newWorkOrder.BasedOnRequirementID = req.ID
                        newWorkOrder.Description = "Based on: " & req.Name
                        newWorkOrder.IsPosted = False
                        newWorkOrder.RequirementType = req.Type
                        newWorkOrder.ReviewType = assetReq.ReviewType
                        newWorkOrder.Status = WorkOrderStatusType.AutoCreated
                        newWorkOrder.DateCreated = GetTimeCreationByRequirement(req, assetTime, assetTimeZoneID)
                        newWorkOrder.DateOpened = newWorkOrder.DateCreated
                        newWorkOrder.DateDue = GetDueTimeByRequirement(assetToCheck, req, assetTime, assetTimeZoneID)

                        If newWorkOrder.ReviewType = RequirementReviewType.Required Or newWorkOrder.ReviewType = RequirementReviewType.RequiredOnlyForException Then
                            For Each g In assetReq.ReviewGroups
                                newWorkOrder.ReviewGroups.Add(g)
                            Next
                        End If
                        For Each g In assetReq.Groups
                            newWorkOrder.Groups.Add(g)
                        Next

                        For Each measure In req.Measures
                            newWorkOrder.Measures.Add(WorkOrderMeasure.From(newWorkOrder, measure))
                        Next
                        Dim measureComments As New WorkOrderMeasure()
                        measureComments.MeasureDescription = "Comments"
                        measureComments.MeasureType = MeasureType.Text
                        newWorkOrder.Measures.Add(measureComments)
                        Details.Add(String.Format("Automatic work order creation for asset: {0} with requiremnt {1}", assetToCheck.AssetName, req.Name))
                        WorkOrder.Insert(newWorkOrder)
                        ModificationHistory.Insert(EntityType.WorkOrder, String.Format("Work Order #{0}", newWorkOrder.ID), _
                                           ModificationAction.Created, 9, Details, _
                                           newWorkOrder.ID, assetToCheck.SiteDepartment.ParentID)

                        NotificationManager.CreateNotificationsByTrigger(NotificationTrigger.WorkOrder_AutomaticCreate, newWorkOrder.ID)
                    Else
                        logger.ErrorFormat("Cannot find AssetRequirement for AssetID={0}, RequirementID={1}", assetToCheck.AssetID, req.ID)
                    End If

                End If

            End If
        Next
    End Sub

    Private Shared Function IsStartDateAllow(ByRef assetToCheck As Asset, ByVal req As Requirement) As Boolean

        Try
            Dim requirementLink As AssetRequirement = assetToCheck.AssetRequirements.SingleOrDefault(Function(r) r.RequirementID = req.ID)
            If requirementLink IsNot Nothing Then
                Return requirementLink.StartDate < DateTime.UtcNow
            End If
        Catch ex As Exception

        End Try

        Return False
    End Function

    Private Shared Function GetTimeCreationByRequirement(ByVal req As Requirement, ByVal assetTime As DateTime, ByVal assetTimeZoneID As String) As DateTime
        Dim result As DateTime
        Select Case req.FrequencyType
            Case RequirementFrequency.TwiceDaily
                Return DateTime.UtcNow
            Case RequirementFrequency.EveryXHours
                result = DateTime.UtcNow.Date.AddHours(DateTime.UtcNow.Hour + req.Frequency).AddMinutes(-req.FrequencyDueEnd)
                Return result
            Case RequirementFrequency.EveryXDays
                result = DateTime.UtcNow.Date.AddDays(req.Frequency * 1).AddMinutes(-req.FrequencyDueEnd)
            Case RequirementFrequency.EveryXMonth
                result = DateTime.UtcNow.Date.AddDays(-DateTime.UtcNow.Day + 1).AddMonths(req.Frequency * 1).AddDays(-req.FrequencyDueEnd)
            Case RequirementFrequency.EveryXYears
                result = New DateTime(DateTime.UtcNow.Year + 1, 1, 1).AddYears(1 - req.Frequency).AddDays(-req.FrequencyDueEnd)
            Case RequirementFrequency.EveryXWeeks
                result = DateTime.UtcNow.Date.AddDays(7 - DateTime.UtcNow.DayOfWeek).AddDays(7 * (req.Frequency - 1)).AddDays(-req.FrequencyDueEnd + 1)
        End Select
        Return TimeZoneManager.ConvertFromCustomTimeFor(result, assetTimeZoneID)
    End Function

    Private Shared Function GetDueTimeByRequirement(ByRef assetToCheck As Asset, ByVal req As Requirement, ByVal assetTime As DateTime, ByVal assetTimeZoneID As String) As DateTime
        Dim result As DateTime
        Select Case req.FrequencyType
            Case RequirementFrequency.TwiceDaily
                If assetTime.Hour < req.FirstTime.Value.Hour Or (assetTime.Hour = req.FirstTime.Value.Hour AndAlso assetTime.Minute < req.FirstTime.Value.Minute) Then
                    result = DateTime.UtcNow.Date.AddHours(req.SecondTime.Value.Hour).AddMinutes(req.SecondTime.Value.Minute)
                Else
                    result = DateTime.UtcNow.Date.AddDays(1).AddMilliseconds(-1)
                End If
            Case RequirementFrequency.EveryXHours
                Return DateTime.UtcNow.Date.AddHours(DateTime.UtcNow.Hour + req.Frequency).AddMilliseconds(-1)
            Case RequirementFrequency.EveryXDays
                result = DateTime.UtcNow.Date.AddDays(req.Frequency * 1).AddMilliseconds(-1)
            Case RequirementFrequency.EveryXMonth
                result = DateTime.UtcNow.Date.AddDays(-DateTime.UtcNow.Day + 1).AddMonths(req.Frequency * 1).AddMilliseconds(-1)
            Case RequirementFrequency.EveryXYears
                result = New DateTime(DateTime.UtcNow.Year + 1, 1, 1).AddYears(1 - req.Frequency).AddMilliseconds(-1)
            Case RequirementFrequency.EveryXWeeks
                result = DateTime.UtcNow.Date.AddDays(7 - DateTime.UtcNow.DayOfWeek + 1).AddDays(7 * (req.Frequency - 1)).AddMilliseconds(-1)
        End Select
        Return TimeZoneManager.ConvertFromCustomTimeFor(result, assetTimeZoneID)
    End Function
#End Region

End Class
